{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Car Rental 1\n",
    "\n",
    "## Objective and Prerequisites\n",
    "\n",
    "Boost your modeling skills with this example, which will teach you how you can use mathematical optimization to figure out how many cars a car rental company should own and where they should be located every day to maximize weekly profits.\n",
    "\n",
    "This model is example 25 from the fifth edition of Model Building in Mathematical Programming by H. Paul Williams on pages 284-286 and 340-342.\n",
    "\n",
    "This example is at the intermediate level, where we assume that you know Python and the Gurobi Python API and that you have some knowledge of building mathematical optimization models.\n",
    "\n",
    "**Download the Repository** <br /> \n",
    "You can download the repository containing this and other examples by clicking [here](https://github.com/Gurobi/modeling-examples/archive/master.zip). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Problem Description\n",
    "\n",
    "A small car rental company (which rents only one type of car) has depots in Glasgow, Manchester, Birmingham and Plymouth. There is an estimated demand for each day of the week except Sunday (when the company is closed). These estimates are given in the following table. It is not necessary to meet all demand.\n",
    "\n",
    "![weeklyDemand](weeklyDemand.PNG)\n",
    "\n",
    "\n",
    "Cars can be rented for one, two or three days and returned to either the\n",
    "depot from which they were rented or another depot at the start of the next morning. For\n",
    "example, a 2-day rental on Thursday means that the car has to be returned on\n",
    "Saturday morning; a 3-day rental on Friday means that the car has to be returned\n",
    "on Tuesday morning. A 1-day rental on Saturday means that the car has to be\n",
    "returned on Monday morning, and a 2-day rental on Tuesday morning.\n",
    "\n",
    "The rental period is independent of the origin and destination. From historical\n",
    "data, the company knows the distribution of rental periods: 55% of cars are hired\n",
    "for one day, 20% for two days, and 25% for three days. The current estimates\n",
    "of percentages of cars rented from each depot and returned to a given depot\n",
    "(independent of the day) are given in the following table.\n",
    "\n",
    "![FromToPct](FromToPct.PNG)\n",
    "\n",
    "The company's marginal cost of renting out a car (‘wear and tear’, administration, etc.) is estimated as follows:\n",
    "\n",
    "| Days rented | Marginal cost |\n",
    "| --- | --- |\n",
    "| 1-day | $\\$ 20$ |\n",
    "| 2-day | $\\$ 25$ |\n",
    "| 3-day | $\\$ 30$ |\n",
    "\n",
    "The ‘opportunity cost’ (interest on capital, storage, servicing, etc.) of owning\n",
    "a car is $\\$ 15$ per week.\n",
    "\n",
    "It is possible to transfer undamaged cars from one depot to another depot,\n",
    "irrespective of distance. Cars cannot be rented out during the day in which they\n",
    "are transferred. The costs in USD, per car, of transfer are given in the following table.\n",
    "\n",
    "![FromToCst](FromToCst.PNG)\n",
    "\n",
    "Ten percent of cars returned by customers are damaged. When this happens,\n",
    "the customer is charged an excess of $\\$ 100$  (irrespective of the amount of damage\n",
    "that the company completely covers by its insurance). In addition, the car has\n",
    "to be transferred to a repair depot, where it will be repaired the following day.\n",
    "The cost of transferring a damaged car is the same as transferring an undamaged\n",
    "one (except when the repair depot is the current depot, \n",
    "in which case the cost would be $\\$0$). The transfer of a damaged car takes a day, unless it is already at a repair depot.\n",
    "Having arrived at a repair depot, all types of repair (or replacement) take a day.\n",
    "Only two of the depots have repair capacity. The (cars/day) capacity at each repair depot is as follows:\n",
    "\n",
    "| Repair depot | Capacity |\n",
    "| --- | --- |\n",
    "| Manchester | 12 |\n",
    "| Birmingham | 20 |\n",
    "\n",
    "Having been repaired, the car is available for rent at the depot the next day\n",
    "or may be transferred to another depot (taking a day). Thus, a car that is returned\n",
    "damaged on a Wednesday morning is transferred to a repair depot (if not the\n",
    "current depot) on Wednesday, repaired on Thursday, and is available for rent\n",
    "at the repair depot on Friday morning.\n",
    "The rental price depends on the number of days for which the car is rented\n",
    "and whether it is returned to the same depot or not. The prices (in USD) are given in the following table.\n",
    "\n",
    "![RentalPrice](RentalPrice.PNG)\n",
    "\n",
    "We assume the following at the beginning of each day:\n",
    "1. Customers return cars that are due that day.\n",
    "2. Damaged cars are sent to the repair depot.\n",
    "3. Cars that were transferred from other depots arrive.\n",
    "4. Transfers are sent out.\n",
    "5. Cars are rented out.\n",
    "6. If it is a repair depot, then the repaired cars are available for rental.\n",
    "\n",
    "The goal is to determine the numbers of cars the car rental company should own and where should they be located at the start of each week day in order to maximize weekly profit. The company wants a ‘steady state’ solution\n",
    "in which the same expected number of cars will be located at the same depot on\n",
    "the same day of subsequent weeks."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Model Formulation\n",
    "\n",
    "$d,d2 \\in \\text{Depots}=\\{\\text{Glasgow}, \\text{Manchester}, \\text{Birmingham},  \\text{Plymouth}\\}$\n",
    "\n",
    "$\\text{NRD}=\\{\\text{Glasgow}, \\text{Plymouth}\\}$: Depots without repair capacity.\n",
    "\n",
    "$\\text{RD}=\\{\\text{Manchester}, \\text{Birmingham}\\}$: Depots with repair capacity.\n",
    "\n",
    "$t \\in \\text{Days}=\\{\\text{Monday},\\text{Tuesday},\\text{Wednesday},\\text{Thursday},\\text{Friday},\\text{Saturday}\\}$\n",
    "\n",
    "$r \\in \\text{RentDays}=\\{1,2,3\\}$: Number of days rented.\n",
    "\n",
    "### Parameters\n",
    "\n",
    "$\\text{demand}_{d,t} \\in \\mathbb{R}^+$: Estimated rental demand at depot $d$ on day $t$.\n",
    "\n",
    "$\\text{pctDepot}_{d,d2} \\in \\mathbb{R}^+$: Proportion of cars rented at depot $d$ to be returned to depot $d2$.\n",
    "\n",
    "$\\text{cstTransfer}_{d,d2} \\in \\mathbb{R}^+$: Transfer cost of a car from depot $d$ to depot $d2$.\n",
    "\n",
    "$\\text{pctRent}_{r} \\in \\mathbb{R}^+$: Proportion of cars rented for $r$ days.\n",
    "\n",
    "$\\text{capRepair}_{d} \\in \\mathbb{R}^+$: Repair capacity of depot $d$.\n",
    "\n",
    "$\\text{cstSameDepot}_{r} \\in \\mathbb{R}^+$: Rental cost for $r$ days with return to same depot.\n",
    "\n",
    "$\\text{cstOtherDepot}_{r} \\in \\mathbb{R}^+$: Rental cost for $r$ days with return to other depot.\n",
    "\n",
    "$\\text{marginalCost}_{r} \\in \\mathbb{R}^+$: Marginal cost to company of  $r$ days rental of a car.\n",
    "\n",
    "$\\text{pctUndamaged } \\in [0,1]$: Percent of cars returned by customers that are undamaged.\n",
    "\n",
    "$\\text{pctDamaged  } \\in [0,1]$: Percent of cars returned by customers that are damaged.\n",
    "\n",
    "$\\text{cstOwn} \\in \\mathbb{R}^+$: Cost of owning a car.\n",
    "\n",
    "$\\text{damagedFee} = 10$: Damaged car fee. Ten percent of the cars are damaged and the fee for a damaged car is $\\$100$.\n",
    "\n",
    "### Decision Variables\n",
    "\n",
    "$\\text{xOwned} \\in \\mathbb{R}^+$: Total number of cars owned.\n",
    "\n",
    "$\\text{xUndamaged}_{d,t} \\in \\mathbb{R}^+$: Number of undamaged cars available at depot $d$ at the beginning of day $t$.\n",
    "\n",
    "$\\text{xDamaged}_{d,t} \\in \\mathbb{R}^+$: Number of damaged cars available at depot $d$ at the beginning of day $t$.\n",
    "\n",
    "$\\text{xRented}_{d,t} \\in \\mathbb{R}^+$: number of cars rented  out from depot $d$ at the beginning of day $t$.\n",
    "\n",
    "$\\text{xUDleft}_{d,t} \\in \\mathbb{R}^+$: Number of undamaged cars available at depot $d$ at the beginning of day $t$.\n",
    "\n",
    "$\\text{xDleft}_{d,t} \\in \\mathbb{R}^+$: Number of damaged cars left in depo $d$ at the end of day  $t$.\n",
    "\n",
    "$\\text{xUDtransfer}_{d,d2,t} \\in \\mathbb{R}^+$: Number of undamaged cars  at depot $d$ at the beginning of day $t$, to be transferred to depot $d2$. \n",
    "\n",
    "$\\text{xDtransfer}_{d,d2,t} \\in \\mathbb{R}^+$: Number of damaged cars at depot $d$ at the beginning of day $t$, to be transferred to depot $d2$. \n",
    "\n",
    "$\\text{xRepaired}_{d,t} \\in \\mathbb{R}^+$: Number of damaged cars to be repaired at depot $d$ during day $t$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Objective function\n",
    "The objective is to maximize profit.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{d \\in \\text{Depots}}\n",
    "\\sum_{t \\in \\text{Days}}\n",
    "\\sum_{r \\in \\text{RentDays}} \n",
    "\\text{pctDepot}_{d,d}*\\text{pctRent}_{r}*(\\text{cstSameDepot}_{r} - \\text{marginalCost}_{r} + \\text{damagedFee})*\\text{xRented}_{d,t}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "+ \\sum_{d \\in \\text{Depots}} \\sum_{d2 \\in \\text{Depots}}\n",
    "\\sum_{t \\in \\text{Days}}\n",
    "\\sum_{r \\in \\text{RentDays}} \n",
    "\\text{pctDepot}_{d,d2}*\\text{pctRent}_{r}*(\\text{cstOtherDepot}_{r} - \\text{marginalCost}_{r} + \\text{damagedFee})*\\text{xRented}_{d,t}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "- \\sum_{d \\in \\text{Depots}} \\sum_{d2 \\in \\text{Depots}}\n",
    "\\sum_{t \\in \\text{Days}} \\text{cstTransfer}_{d,d2}*(\\text{xUDtransfer}_{d,d2,t} + \\text{xDtransfer}_{d,d2,t} )\n",
    "- \\text{cstOwn}*\\text{xOwned}\n",
    "\\end{equation}\n",
    "\n",
    "\n",
    "### Constraints\n",
    "\n",
    "**Undamaged cars at a non-repair depot** <br />\n",
    "Number of undamaged cars available at a non-repair depot $d$ at the beginning of day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{d2 \\in \\text{Depots}} \n",
    "\\sum_{r \\in \\text{RentDays}} \\text{pctUndamaged}*\\text{pctDepot}_{d2,d}*\\text{pctRent}_{r}*\\text{xRented}_{d2,(t-r)mod(6)}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "+ \\sum_{d2 \\in \\text{Depots}} \\text{xUDtransfer}_{d2,d,(t-1)mod(6)} + \\text{xUDleft}_{d,(t-1)mod(6)} = \\text{xUndamaged}_{d,t} \n",
    "\\quad \\forall d \\in NRD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "Demand of undamaged cars at the non-repair depot $d$ during day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{xUndamaged}_{d,t} = \\text{xRented}_{d,t} + \n",
    "\\sum_{d2 \\in \\text{Depots}} \\text{xUDtransfer}_{d,d2,t} + \\text{xUDleft}_{d,t}\n",
    "\\quad \\forall d \\in NRD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "**Undamaged cars at a repair depot** <br />\n",
    "Number of undamaged cars available at a repair depot $d$ at the beginning of day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{d2 \\in \\text{Depots}} \n",
    "\\sum_{r \\in \\text{RentDays}} \\text{pctUndamaged}*\\text{pctDepot}_{d2,d}*\\text{pctRent}_{r}*\\text{xRented}_{d2,(t-r)mod(6)}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "+ \\sum_{d2 \\in \\text{Depots}} \\text{xUDtransfer}_{d2,d,(t-1)mod(6)} \n",
    "+ \\text{xRepaired}_{d,(t-1)mod(6)}  + \\text{xUDleft}_{d,(t-1)mod(6)} = \\text{xUndamaged}_{d,t} \n",
    "\\quad \\forall d \\in NRD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "Demand of undamaged cars at the repair depot $d$ during day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{xUndamaged}_{d,t} = \\text{xRented}_{d,t} + \n",
    "\\sum_{d2 \\in \\text{Depots}} \\text{xUDtransfer}_{d,d2,t} + \\text{xUDleft}_{d,t}\n",
    "\\quad \\forall d \\in NRD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "**Damaged cars at a non-repair depot** <br />\n",
    "Number of damaged cars available at a non-repair depot $d$ at the beginning of day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{d2 \\in \\text{Depots}} \n",
    "\\sum_{r \\in \\text{RentDays}} \\text{pctDamaged}*\\text{pctDepot}_{d2,d}*\\text{pctRent}_{r}*\\text{xRented}_{d2,(t-r)mod(6)}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "+ \\text{xDleft}_{d,(t-1)mod(6)} = \\text{xDamaged}_{d,t} \\quad \\forall d \\in NRD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "Demand of undamaged cars at the non-repair depot $d$ during day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{xDamaged}_{d,t} = \n",
    "\\sum_{d2 \\in \\text{Depots} \\cap RD} \\text{xDtransfer}_{d,d2,t} + \\text{xDleft}_{d,t}\n",
    "\\quad \\forall d \\in NRD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "**Damaged cars at a repair depot** <br />\n",
    "Number of damaged cars available at a repair depot $d$ at the beginning of day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{d2 \\in \\text{Depots}} \n",
    "\\sum_{r \\in \\text{RentDays}} \\text{pctDamaged}*\\text{pctDepot}_{d2,d}*\\text{pctRent}_{r}*\\text{xRented}_{d2,(t-r)mod(6)}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "+ \\sum_{d2 \\in \\text{Depots}} \\text{xDtransfer}_{d2,d,(t-1)mod(6)} \n",
    "+ \\text{xDleft}_{d,(t-1)mod(6)} = \\text{xdamaged}_{d,t} \n",
    "\\quad \\forall d \\in RD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "Demand of undamaged cars at the non-repair depot $d$ during day $t$.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{xDamaged}_{d,t} = \\text{xRepaired}_{d,t} +\n",
    "\\sum_{d2 \\in \\text{Depots} \\cap RD} \\text{xDtransfer}_{d,d2,t} + \\text{xDleft}_{d,t}\n",
    "\\quad \\forall d \\in RD, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "**Depot Capacity** <br />\n",
    "Repair capacity of depot $d$ for each day $t$. \n",
    "\n",
    "\\begin{equation}\n",
    "\\text{xRepaired}_{d,t} \\leq \\text{capRepair}_{d}\n",
    "\\quad \\forall d \\in Depots, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "**Depot Demand** <br />\n",
    "Demand at depot $d$ for each day $t$. \n",
    "\n",
    "\\begin{equation}\n",
    "\\text{xRented}_{d,t} \\leq \\text{demand}_{d,t}\n",
    "\\quad \\forall d \\in Depots, t \\in Days\n",
    "\\end{equation}\n",
    "\n",
    "**Number of cars** <br />\n",
    "Total number of cars owned equals number of cars rented out from all depots on Monday for 3 days, plus those on Tuesday for 2 or 3 days, plus all damaged and undamaged cars in depots at the beginning of Wednesday. \n",
    "Rationale: Let’s pick a day (Wednesday), count the cars undamaged and damaged that were returned to the depots and that are available on Wednesday morning. Let’s count the cars that have been rented and have not been returned: Cars rented on Monday for 3 days, and cars rented on Tuesday for 2 or 3 days.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{d \\in \\text{Depots}} (0.25*\\text{xRented}_{d,0} + 0.45*\\text{xRented}_{d,1} + \\text{xUndamaged}_{d,2}  + \\text{xdamaged}_{d,2} ) = \\text{xOwned}\n",
    "\\end{equation}\n",
    "\n",
    "---\n",
    "## Python Implementation\n",
    "\n",
    "We import the Gurobi Python Module and other Python libraries."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install gurobipy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from itertools import product\n",
    "\n",
    "import gurobipy as gp\n",
    "from gurobipy import GRB\n",
    "\n",
    "# tested with Python 3.11 & Gurobi 11.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Input data\n",
    "\n",
    "We define all the input data for the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# list of depots and working days of a week\n",
    "\n",
    "depots = ['Glasgow','Manchester','Birmingham','Plymouth']\n",
    "NRD = ['Glasgow','Plymouth'] # Non-repair depot\n",
    "RD =['Manchester','Birmingham'] # Repair depot\n",
    "\n",
    "days = [0,1,2,3,4,5] # Monday = 0, Tuesday = 1, ...  Saturday = 5\n",
    "rentDays = [1,2,3]\n",
    "\n",
    "d2w, demand = gp.multidict({\n",
    "    ('Glasgow',0): 100,\n",
    "    ('Glasgow',1): 150,\n",
    "    ('Glasgow',2): 135,\n",
    "    ('Glasgow',3): 83,\n",
    "    ('Glasgow',4): 120,\n",
    "    ('Glasgow',5): 230,\n",
    "    ('Manchester',0): 250,\n",
    "    ('Manchester',1): 143,\n",
    "    ('Manchester',2): 80,\n",
    "    ('Manchester',3): 225,\n",
    "    ('Manchester',4): 210,\n",
    "    ('Manchester',5): 98,\n",
    "    ('Birmingham',0): 95,\n",
    "    ('Birmingham',1): 195,\n",
    "    ('Birmingham',2): 242,\n",
    "    ('Birmingham',3): 111,\n",
    "    ('Birmingham',4): 70,\n",
    "    ('Birmingham',5): 124,\n",
    "    ('Plymouth',0): 160,\n",
    "    ('Plymouth',1): 99,\n",
    "    ('Plymouth',2): 55,\n",
    "    ('Plymouth',3): 96,\n",
    "    ('Plymouth',4): 115,\n",
    "    ('Plymouth',5): 80\n",
    "})\n",
    "\n",
    "#repairCap\n",
    "depots, capacity = gp.multidict({\n",
    "    ('Glasgow'): 0,\n",
    "    ('Manchester'): 12,\n",
    "    ('Birmingham'): 20,\n",
    "    ('Plymouth'): 0\n",
    "})\n",
    "\n",
    "# Create a dictionary to capture \n",
    "# pctRent: percentage of cars rented for r days \n",
    "# cstMarginal: marginal cost for renting a car for r days\n",
    "# prcSameD: price of renting a car r days and returning to same depot\n",
    "# prcOtherD: price of renting a car r days and returning to another depot\n",
    "rentDays, pctRent, costMarginal, priceSameD, priceOtherD = gp.multidict({\n",
    "    (1): [0.55,20,50,70],\n",
    "    (2): [0.20,25,70,100],\n",
    "    (3): [0.25,30,120,150]\n",
    "})\n",
    "\n",
    "# Cost of owing a car per week.\n",
    "cstOwn = 15\n",
    "\n",
    "# Proportional damaged car fee\n",
    "damagedFee = 10\n",
    "\n",
    "# Create a dictionary to capture the proportion of cars rented at depot d to be returned to depot d2 \n",
    "d2d, pctFromToD = gp.multidict({\n",
    "    ('Glasgow','Glasgow'): 0.6,\n",
    "    ('Glasgow','Manchester'): 0.2,\n",
    "    ('Glasgow','Birmingham'): 0.1,\n",
    "    ('Glasgow','Plymouth'): 0.1,\n",
    "    ('Manchester','Glasgow'): 0.15,\n",
    "    ('Manchester','Manchester'): 0.55,\n",
    "    ('Manchester','Birmingham'): 0.25,\n",
    "    ('Manchester','Plymouth'): 0.05,\n",
    "    ('Birmingham','Glasgow'): 0.15,\n",
    "    ('Birmingham','Manchester'): 0.2,\n",
    "    ('Birmingham','Birmingham'): 0.54,\n",
    "    ('Birmingham','Plymouth'): 0.11,\n",
    "    ('Plymouth','Glasgow'): 0.08,\n",
    "    ('Plymouth','Manchester'): 0.12,\n",
    "    ('Plymouth','Birmingham'): 0.27,\n",
    "    ('Plymouth','Plymouth'): 0.53\n",
    "})\n",
    "\n",
    "# Create a dictionary to capture the transfer costs  of cars\n",
    "d2d, cstFromToD = gp.multidict({\n",
    "    ('Glasgow','Glasgow'): 0.001,\n",
    "    ('Glasgow','Manchester'): 20,\n",
    "    ('Glasgow','Birmingham'): 30,\n",
    "    ('Glasgow','Plymouth'): 50,\n",
    "    ('Manchester','Glasgow'): 20,\n",
    "    ('Manchester','Manchester'): 0.001,\n",
    "    ('Manchester','Birmingham'): 15,\n",
    "    ('Manchester','Plymouth'): 35,\n",
    "    ('Birmingham','Glasgow'): 30,\n",
    "    ('Birmingham','Manchester'): 15,\n",
    "    ('Birmingham','Birmingham'): 0.001,\n",
    "    ('Birmingham','Plymouth'): 25,\n",
    "    ('Plymouth','Glasgow'): 50,\n",
    "    ('Plymouth','Manchester'): 35,\n",
    "    ('Plymouth','Birmingham'): 25,\n",
    "    ('Plymouth','Plymouth'): 0.001\n",
    "})\n",
    "\n",
    "# Proportion of undamaged and damaged cars returned\n",
    "pctUndamaged = 0.9\n",
    "pctDamaged = 0.1\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Preprocessing\n",
    "We prepare the data structures to build the linear programming model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Build a list of tuples (depot, depot2) such that d != d2\n",
    "list_d2notd = []\n",
    "\n",
    "for d,d2 in d2d:\n",
    "    if (d != d2):\n",
    "        tp = d,d2\n",
    "        list_d2notd.append(tp)\n",
    "\n",
    "d2notd = gp.tuplelist(list_d2notd)\n",
    "\n",
    "# Build a list of tuples (depot, depot2, day)\n",
    "list_dd2t = []\n",
    "\n",
    "for d,d2 in d2notd:\n",
    "    for t in days:\n",
    "        tp = d,d2,t \n",
    "        list_dd2t.append(tp)\n",
    "                    \n",
    "dd2t = gp.tuplelist(list_dd2t)\n",
    "\n",
    "# Build a list of tuples (depot, rent_day)\n",
    "list_dr = []\n",
    "\n",
    "for d in depots:\n",
    "    for r in rentDays:\n",
    "        tp = d,r\n",
    "        list_dr.append(tp)\n",
    "        \n",
    "dr = gp.tuplelist(list_dr) \n",
    "\n",
    "# Build a list of tuples (depot, day, rent_days )\n",
    "list_dtr = []\n",
    "\n",
    "for d in depots:\n",
    "    for t in days:\n",
    "            for r in rentDays:\n",
    "                tp = d,t,r\n",
    "                list_dtr.append(tp)\n",
    "                \n",
    "dtr = gp.tuplelist(list_dtr) \n",
    "\n",
    "# Build a list of tuples (depot, depot2, day, rent_days)\n",
    "list_dd2tr = []\n",
    "\n",
    "for d,d2 in d2notd:\n",
    "    for t in days:\n",
    "        for r in rentDays:\n",
    "            tp = d,d2,t,r\n",
    "            list_dd2tr.append(tp)\n",
    "                    \n",
    "                    \n",
    "dd2tr = gp.tuplelist(list_dd2tr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Deployment\n",
    "We create a model and the variables. The main decision variables are the number of cars to own\n",
    "and where should they be located at the start of each day of a week to maximize weekly profits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using license file c:\\gurobi\\gurobi.lic\n"
     ]
    }
   ],
   "source": [
    "model = gp.Model('RentalCar1')\n",
    "\n",
    "# Number of cars owned\n",
    "n = model.addVar(name=\"cars\")\n",
    "\n",
    "# Number of undamaged cars\n",
    "nu = model.addVars(d2w, name=\"UDcars\")\n",
    "\n",
    "# Number of damaged cars\n",
    "nd = model.addVars(d2w, name=\"Dcars\")\n",
    "\n",
    "# Number of cars hired (rented) cannot exceed their demand\n",
    "tr = model.addVars(d2w, ub=demand, name=\"Hcars\")\n",
    "#for d,t in d2w:\n",
    "    #tr[d,t].lb = 1\n",
    "\n",
    "# End inventory of undamaged cars\n",
    "eu = model.addVars(d2w, name=\"EUDcars\")\n",
    "\n",
    "# End inventory of damaged cars\n",
    "ed = model.addVars(d2w, name=\"EDcars\")\n",
    "\n",
    "# Number of undamaged cars transferred\n",
    "tu = model.addVars(dd2t, name=\"TUDcars\")\n",
    "\n",
    "# Number of damaged cars transferred\n",
    "td = model.addVars(dd2t, name=\"TDcars\")\n",
    "\n",
    "# Number of damaged cars repaired\n",
    "rp = model.addVars(d2w, name=\"RPcars\")\n",
    "\n",
    "# Number of damaged cars repaired cannot exceed depot capacity\n",
    "for d,t in d2w:\n",
    "    rp[d,t].ub = capacity[d] #repair capacity"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Constraints\n",
    "The number of undamaged cars available at a non-repair depot d at the beginning of  day t should be equal to the demand of undamaged cars at the non-repair depot d during day t."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Undamaged cars into a non-repair depot constraints (left hand side of balance equation -availability)\n",
    "\n",
    "UDcarsNRD_L = model.addConstrs((gp.quicksum(pctUndamaged*pctFromToD[d2,d]*pctRent[r]*tr[d2,(t-r)%6 ] for d2,r in dr ) \n",
    "                              + gp.quicksum(tu.select('*',d,(t-1)%6)  ) \n",
    "                              + eu[d,(t-1)%6 ] == nu[d,t] for d in NRD for t in days ), \n",
    "                             name=\"UDcarsNRD_L\")\n",
    "\n",
    "# Undamaged cars out of a non-repair depot constraints (right hand side of balance equation -requirements)\n",
    "\n",
    "UDcarsNRD_R = model.addConstrs((tr[d,t] \n",
    "                                + gp.quicksum(tu.select(d,'*',t )) \n",
    "                                + eu[d,t] == nu[d,t] for d in NRD for t in days ), name='UDcarsNRD_R' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The number of undamaged cars available at a repair depot d at the beginning of  day t should be equal to the demand of undamaged cars at the repair depot d during day t."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Undamaged cars into a repair depot constraints (left hand side of balance equation -availability)\n",
    "\n",
    "UDcarsRD_L = model.addConstrs((gp.quicksum(pctUndamaged*pctFromToD[d2,d]*pctRent[r]*tr[d2,(t-r)%6 ] for d2,r in dr ) \n",
    "                              + gp.quicksum(tu.select('*',d,(t-1)%6)  ) + rp[d, (t-1)%6 ]\n",
    "                              + eu[d,(t-1)%6 ] == nu[d,t] for d in RD for t in days ), \n",
    "                             name=\"UDcarsRD_L\")\n",
    "\n",
    "# Undamaged cars out of a repair depot constraints (right hand side of balance equation -requirements)\n",
    "\n",
    "UDcarsRD_R = model.addConstrs((tr[d,t] \n",
    "                                + gp.quicksum(tu.select(d,'*',t ) ) \n",
    "                                + eu[d,t] == nu[d,t] for d in RD for t in days ), name='UDcarsRD_R' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The number of damaged cars available at a non-repair depot d at the beginning of  day t should be equal to the demand of damaged cars at the non-repair depot d during day t."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Damaged cars into a non-repair depot constraints (left hand side of balance equation -availability)\n",
    "\n",
    "DcarsNRD_L = model.addConstrs((gp.quicksum(pctDamaged*pctFromToD[d2,d]*pctRent[r]*tr[d2,(t-r)%6 ] for d2,r in dr ) \n",
    "                              + ed[d,(t-1)%6 ] == nd[d,t] for d in NRD for t in days ), \n",
    "                             name=\"DcarsNRD_L\")\n",
    "\n",
    "# Damaged cars out of a non-repair depot constraints (right hand side of balance equation -requirements)\n",
    "\n",
    "DcarsNRD_R = model.addConstrs(( gp.quicksum(td[d,d2,t] for d2 in RD ) \n",
    "                                + ed[d,t] == nd[d,t] for d in NRD for t in days ), name='DcarsNRD_R' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The number of damaged cars available at a repair depot d at the beginning of  day t should be equal to the demand of damaged cars at the repair depot d during day t."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Damaged cars into a repair depot constraints (left hand side of balance equation -availability)\n",
    "\n",
    "DcarsRD_L = model.addConstrs((gp.quicksum(pctDamaged*pctFromToD[d2,d]*pctRent[r]*tr[d2,(t-r)%6 ] for d2,r in dr )\n",
    "                              + gp.quicksum(td[d2,d,(t-1)%6 ] for d2, dd in d2notd if (dd == d)) \n",
    "                              + ed[d,(t-1)%6 ] == nd[d,t] for d in RD for t in days ), \n",
    "                             name=\"DcarsRD_L\")\n",
    "\n",
    "# Damaged cars out of a repair depot constraints (right hand side of balance equation -requirements)\n",
    "\n",
    "DcarsND_R = model.addConstrs((rp[d,t] + gp.quicksum(td[d,d2,t ] for d2 in NRD ) \n",
    "                                + ed[d,t] == nd[d,t] for d in RD for t in days ), name='DcarsND_R' )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Total number of cars equals the number of cars rented out from all depots on Monday for 3 days, plus those on Tuesday for 2 or 3 days, plus all damaged and undamaged cars in depots at the beginning of Wednesday."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Total number of cars owned constraint\n",
    "# Note: 25% of cars are rented for 3 days, and 20% + 25% = 45% of the cars are rented for 2-days or 3-days\n",
    "\n",
    "carsConstr = model.addConstr((gp.quicksum(0.25*tr[d,0] + 0.45*tr[d,1] + nu[d,2] + nd[d,2] for d in depots ) \n",
    "                              == n ),name='carsConstr')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The objective function is to maximize profit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Maximize profit objective function\n",
    "\n",
    "model.setObjective((\n",
    "    gp.quicksum(pctFromToD[d,d]*pctRent[r]*(priceSameD[r] - costMarginal[r] + damagedFee)*tr[d,t] for d,t,r in dtr )\n",
    "    + gp.quicksum(pctFromToD[d,d2]*pctRent[r]*(priceOtherD[r]-costMarginal[r]+damagedFee)*tr[d,t] for d,d2,t,r in dd2tr)\n",
    "    - gp.quicksum(cstFromToD[d,d2]*tu[d,d2,t] for d,d2,t in dd2t) \n",
    "    - gp.quicksum(cstFromToD[d,d2]*td[d,d2,t] for d,d2,t in dd2t) - cstOwn*n ), GRB.MAXIMIZE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gurobi Optimizer version 9.1.0 build v9.1.0rc0 (win64)\n",
      "Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n",
      "Optimize a model with 97 rows, 289 columns and 1061 nonzeros\n",
      "Model fingerprint: 0x7ddb81fe\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e-03, 1e+00]\n",
      "  Objective range  [2e+01, 7e+01]\n",
      "  Bounds range     [1e+01, 3e+02]\n",
      "  RHS range        [0e+00, 0e+00]\n",
      "Presolve removed 49 rows and 85 columns\n",
      "Presolve time: 0.01s\n",
      "Presolved: 48 rows, 204 columns, 936 nonzeros\n",
      "\n",
      "Iteration    Objective       Primal Inf.    Dual Inf.      Time\n",
      "       0    2.1102412e+05   3.626166e+02   0.000000e+00      0s\n",
      "      69    1.2116021e+05   0.000000e+00   0.000000e+00      0s\n",
      "\n",
      "Solved in 69 iterations and 0.01 seconds\n",
      "Optimal objective  1.211602072e+05\n"
     ]
    }
   ],
   "source": [
    "# Verify model formulation\n",
    "\n",
    "model.write('CarRental1.lp')\n",
    "\n",
    "# Run optimization engine\n",
    "\n",
    "model.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Analysis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The optimal number of cars to be owned is: 617.\n",
      "The optimal profit is: $121,160.21.\n"
     ]
    }
   ],
   "source": [
    "# Output report\n",
    "\n",
    "# Total number of cars owned\n",
    "print(f\"The optimal number of cars to be owned is: {round(n.x)}.\")\n",
    "\n",
    "# Optimal profit\n",
    "print(f\"The optimal profit is: {'${:,.2f}'.format(round(model.objVal,2))}.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "_________________________________________________________________________________\n",
      "Estimated number of undamaged cars in depot at the beginning of each day: \n",
      "_________________________________________________________________________________\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Day</th>\n",
       "      <th>Glasgow</th>\n",
       "      <th>Manchester</th>\n",
       "      <th>Birmingham</th>\n",
       "      <th>Plymouth</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Monday</td>\n",
       "      <td>68</td>\n",
       "      <td>98</td>\n",
       "      <td>146</td>\n",
       "      <td>41</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Tuesday</td>\n",
       "      <td>66</td>\n",
       "      <td>95</td>\n",
       "      <td>155</td>\n",
       "      <td>40</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Wednesday</td>\n",
       "      <td>70</td>\n",
       "      <td>100</td>\n",
       "      <td>123</td>\n",
       "      <td>43</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Thursday</td>\n",
       "      <td>68</td>\n",
       "      <td>114</td>\n",
       "      <td>116</td>\n",
       "      <td>42</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Friday</td>\n",
       "      <td>70</td>\n",
       "      <td>102</td>\n",
       "      <td>124</td>\n",
       "      <td>43</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Saturday</td>\n",
       "      <td>67</td>\n",
       "      <td>95</td>\n",
       "      <td>158</td>\n",
       "      <td>40</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Day Glasgow Manchester Birmingham Plymouth\n",
       "     Monday      68         98        146       41\n",
       "    Tuesday      66         95        155       40\n",
       "  Wednesday      70        100        123       43\n",
       "   Thursday      68        114        116       42\n",
       "     Friday      70        102        124       43\n",
       "   Saturday      67         95        158       40"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create a list to translate the number label of each day to the actual name of the day\n",
    "dayname = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']\n",
    "\n",
    "# Number of undamaged cars in depot at the beginning of each day.\n",
    "print(\"\\n\\n_________________________________________________________________________________\")\n",
    "print(f\"Estimated number of undamaged cars in depot at the beginning of each day: \")\n",
    "print(\"_________________________________________________________________________________\")\n",
    "\n",
    "undamaged_cars = pd.DataFrame(\n",
    "    {\n",
    "        \"Day\": [dayname[t] for t in days],\n",
    "        \"Glasgow\": [round(nu['Glasgow',t].x) for t in days],\n",
    "        \"Manchester\": [round(nu['Manchester',t].x) for t in days],\n",
    "        \"Birmingham\": [round(nu['Birmingham',t].x) for t in days],\n",
    "        \"Plymouth\": [round(nu['Plymouth',t].x) for t in days],\n",
    "    }\n",
    ")\n",
    "undamaged_cars.index=[''] * len(undamaged_cars)\n",
    "undamaged_cars"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________________________\n",
      "Estimated number of damaged cars in depot at the beginning of each day: \n",
      "_________________________________________________________________________________\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Day</th>\n",
       "      <th>Glasgow</th>\n",
       "      <th>Manchester</th>\n",
       "      <th>Birmingham</th>\n",
       "      <th>Plymouth</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Monday</td>\n",
       "      <td>8</td>\n",
       "      <td>12</td>\n",
       "      <td>20</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Tuesday</td>\n",
       "      <td>7</td>\n",
       "      <td>12</td>\n",
       "      <td>20</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Wednesday</td>\n",
       "      <td>8</td>\n",
       "      <td>13</td>\n",
       "      <td>20</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Thursday</td>\n",
       "      <td>8</td>\n",
       "      <td>12</td>\n",
       "      <td>21</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Friday</td>\n",
       "      <td>8</td>\n",
       "      <td>12</td>\n",
       "      <td>20</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Saturday</td>\n",
       "      <td>7</td>\n",
       "      <td>12</td>\n",
       "      <td>22</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Day Glasgow Manchester Birmingham Plymouth\n",
       "     Monday       8         12         20        6\n",
       "    Tuesday       7         12         20        4\n",
       "  Wednesday       8         13         20        5\n",
       "   Thursday       8         12         21        5\n",
       "     Friday       8         12         20        7\n",
       "   Saturday       7         12         22        4"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Number of Damaged cars in depot at the beginning of each day.\n",
    "print(\"_________________________________________________________________________________\")\n",
    "print(f\"Estimated number of damaged cars in depot at the beginning of each day: \")\n",
    "print(\"_________________________________________________________________________________\")\n",
    "\n",
    "damaged_cars = pd.DataFrame(\n",
    "    {\n",
    "        \"Day\": [dayname[t] for t in days],\n",
    "        \"Glasgow\": [round(nd['Glasgow',t].x) for t in days],\n",
    "        \"Manchester\": [round(nd['Manchester',t].x) for t in days],\n",
    "        \"Birmingham\": [round(nd['Birmingham',t].x) for t in days],\n",
    "        \"Plymouth\": [round(nd['Plymouth',t].x) for t in days],\n",
    "    }\n",
    ")\n",
    "damaged_cars.index=[''] * len(damaged_cars)\n",
    "damaged_cars"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________________________\n",
      "Estimated number of undamaged cars rented out from each depot and day: \n",
      "_________________________________________________________________________________\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Day</th>\n",
       "      <th>Glasgow</th>\n",
       "      <th>Manchester</th>\n",
       "      <th>Birmingham</th>\n",
       "      <th>Plymouth</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Monday</td>\n",
       "      <td>61</td>\n",
       "      <td>89</td>\n",
       "      <td>86</td>\n",
       "      <td>37</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Tuesday</td>\n",
       "      <td>59</td>\n",
       "      <td>85</td>\n",
       "      <td>140</td>\n",
       "      <td>36</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Wednesday</td>\n",
       "      <td>63</td>\n",
       "      <td>72</td>\n",
       "      <td>111</td>\n",
       "      <td>39</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Thursday</td>\n",
       "      <td>62</td>\n",
       "      <td>103</td>\n",
       "      <td>100</td>\n",
       "      <td>38</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Friday</td>\n",
       "      <td>63</td>\n",
       "      <td>92</td>\n",
       "      <td>63</td>\n",
       "      <td>39</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <td>Saturday</td>\n",
       "      <td>60</td>\n",
       "      <td>85</td>\n",
       "      <td>112</td>\n",
       "      <td>36</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Day Glasgow Manchester Birmingham Plymouth\n",
       "     Monday      61         89         86       37\n",
       "    Tuesday      59         85        140       36\n",
       "  Wednesday      63         72        111       39\n",
       "   Thursday      62        103        100       38\n",
       "     Friday      63         92         63       39\n",
       "   Saturday      60         85        112       36"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Undamaged car rented out from each depot and day.\n",
    "print(\"_________________________________________________________________________________\")\n",
    "print(f\"Estimated number of undamaged cars rented out from each depot and day: \")\n",
    "print(\"_________________________________________________________________________________\")\n",
    "\n",
    "rentedOut = {}\n",
    "\n",
    "for d in depots:\n",
    "    for t in days:\n",
    "        count = 0\n",
    "        for d2 in depots:\n",
    "            for r in rentDays:\n",
    "                #print(f\"Depot {d}, day {t}: cars rented out {tr[d,t].x}\")\n",
    "                count += pctUndamaged*pctFromToD[d,d2]*pctRent[r]*tr[d,t].x\n",
    "        rentedOut[d,t] = round(count)\n",
    "    \n",
    "\n",
    "#print(rentedOut)\n",
    "\n",
    "rentout_cars = pd.DataFrame(\n",
    "    {\n",
    "        \"Day\": [dayname[t] for t in days],\n",
    "        \"Glasgow\": [round(rentedOut['Glasgow',t]) for t in days],\n",
    "        \"Manchester\": [round(rentedOut['Manchester',t]) for t in days],\n",
    "        \"Birmingham\": [round(rentedOut['Birmingham',t]) for t in days],\n",
    "        \"Plymouth\": [round(rentedOut['Plymouth',t]) for t in days],\n",
    "    }\n",
    ")\n",
    "rentout_cars.index=[''] * len(rentout_cars)\n",
    "rentout_cars"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## References\n",
    "\n",
    "H. Paul Williams, Model Building in Mathematical Programming, fifth edition.\n",
    "\n",
    "Copyright © 2020 Gurobi Optimization, LLC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
